﻿namespace Blaze.Security
{
    using System;
    using UnityEngine;

    /// <summary>
    /// 用于内存加密<see cref="Vector2"/>型的变量。
    /// </summary>
    public struct SecureVector2
    {
        /// <summary>
        /// 当检测到内存作弊时调用此方法（只会触发一次，之后会被设为null）
        /// </summary>
        public static Action onCheatingDetected;

        private static int cryptoKey = 20151201;
        private int currentCryptoKey;
        private Vector2 hiddenValue;
        private Vector2 fakeValue;
        private bool isInited;

        private SecureVector2(Vector2 value)
        {
            currentCryptoKey = cryptoKey;
            hiddenValue = value;
            fakeValue = Vector2.zero;
            isInited = true;
        }

        /// <summary>
        /// 获取x轴分量。
        /// </summary>
        public float x
        {
            get
            {
                float decrypted = InternalDecryptField(hiddenValue.x);
                if (onCheatingDetected != null && fakeValue != new Vector2(0, 0) && Math.Abs(decrypted - fakeValue.x) > 0.0005f)
                {
                    onCheatingDetected();
                    onCheatingDetected = null;
                }
                return decrypted;
            }

            set
            {
                hiddenValue.x = InternalEncryptField(value);
                if (onCheatingDetected != null)
                {
                    fakeValue.x = value;
                }
            }
        }

        /// <summary>
        /// 获取y轴分量。
        /// </summary>
        public float y
        {
            get
            {
                float decrypted = InternalDecryptField(hiddenValue.y);
                if (onCheatingDetected != null && fakeValue != new Vector2(0, 0) && Math.Abs(decrypted - fakeValue.y) > 0.0005f)
                {
                    onCheatingDetected();
                    onCheatingDetected = null;
                }
                return decrypted;
            }

            set
            {
                hiddenValue.y = InternalEncryptField(value);
                if (onCheatingDetected != null)
                {
                    fakeValue.y = value;
                }
            }
        }

        /// <summary>
        /// 获取指定索引位的分量。
        /// 0-x轴，1-y轴。
        /// </summary>
        /// <param name="index">索引号</param>
        /// <returns>分量明文</returns>
        public float this[int index]
        {
            get
            {
                switch (index)
                {
                    case 0:
                        return x;
                    case 1:
                        return y;
                    default:
                        throw new IndexOutOfRangeException("index");
                }
            }
            set
            {
                switch (index)
                {
                    case 0:
                        x = value;
                        break;
                    case 1:
                        y = value;
                        break;
                    default:
                        throw new IndexOutOfRangeException("index");
                }
            }
        }

        /// <summary>
        /// 设置新的加密密钥。
        /// </summary>
        /// <param name="newKey">新的密钥</param>
        public static void SetNewCryptoKey(int newKey)
        {
            cryptoKey = newKey;
        }

        /// <summary>
        /// 获取明文。
        /// </summary>
        public Vector2 GetEncrypted()
        {
            if (currentCryptoKey != cryptoKey)
            {
                var temp = InternalDecrypt();
                hiddenValue = Encrypt(temp, cryptoKey);
                currentCryptoKey = cryptoKey;
            }

            return hiddenValue;
        }

        /// <summary>
        /// 根据密文设置变量。
        /// </summary>
        /// <param name="encrypted">密文</param>
        public void SetEncrypted(Vector2 encrypted)
        {
            hiddenValue = encrypted;
            if (onCheatingDetected != null)
            {
                fakeValue = InternalDecrypt();
            }
        }

        /// <summary>
        /// 使用默认的密钥加密变量。
        /// </summary>
        /// <param name="value">明文</param>
        /// <returns>密文</returns>
        public static Vector2 Encrypt(Vector2 value)
        {
            return Encrypt(value, 0);
        }

        /// <summary>
        /// 使用指定的密钥加密变量。
        /// </summary>
        /// <param name="value">明文</param>
        /// <param name="key">密钥</param>
        /// <returns>密文</returns>
        public static Vector2 Encrypt(Vector2 value, int key)
        {
            if (key == 0)
            {
                key = cryptoKey;
            }

            value.x = SecureDouble.Encrypt(value.x, key);
            value.y = SecureDouble.Encrypt(value.y, key);
            return value;
        }

        /// <summary>
        /// 使用默认的密钥解密变量。
        /// </summary>
        /// <param name="value">密文</param>
        /// <returns>明文</returns>
        public static Vector2 Decrypt(Vector2 value)
        {
            return Decrypt(value, 0);
        }

        /// <summary>
        /// 使用指定的密钥解密变量。
        /// </summary>
        /// <param name="value">密文</param>
        /// <param name="key">密钥</param>
        /// <returns>明文</returns>
        public static Vector2 Decrypt(Vector2 value, int key)
        {
            if (key == 0)
            {
                key = cryptoKey;
            }

            value.x = (float) SecureDouble.Decrypt((long) value.x, key);
            value.y = (float) SecureDouble.Decrypt((long) value.y, key);

            return value;
        }

        private Vector2 InternalDecrypt()
        {
            if (!isInited)
            {
                currentCryptoKey = cryptoKey;
                hiddenValue = Encrypt(new Vector2(0, 0));
                fakeValue = new Vector2(0, 0);
                isInited = true;
            }

            var key = cryptoKey;

            if (currentCryptoKey != cryptoKey)
            {
                key = currentCryptoKey;
            }

            Vector2 value;

            value.x = (float) SecureDouble.Decrypt((long) hiddenValue.x, key);
            value.y = (float) SecureDouble.Decrypt((long) hiddenValue.y, key);

            if (onCheatingDetected != null && !fakeValue.Equals(new Vector2(0, 0)) && !value.Equals(fakeValue))
            {
                onCheatingDetected();
                onCheatingDetected = null;
            }

            return value;
        }

        private float InternalDecryptField(float encrypted)
        {
            var key = cryptoKey;

            if (currentCryptoKey != cryptoKey)
            {
                key = currentCryptoKey;
            }

            var result = (float) SecureDouble.Decrypt((long) encrypted, key);
            return result;
        }

        private float InternalEncryptField(float encrypted)
        {
            var result = SecureDouble.Encrypt(encrypted, cryptoKey);
            return result;
        }

        public static implicit operator SecureVector2(Vector2 value)
        {
            var secure = new SecureVector2(Encrypt(value));
            if (onCheatingDetected != null)
            {
                secure.fakeValue = value;
            }
            return secure;
        }

        public static implicit operator Vector2(SecureVector2 value)
        {
            return value.InternalDecrypt();
        }

        public override int GetHashCode()
        {
            return InternalDecrypt().GetHashCode();
        }

        public override string ToString()
        {
            return InternalDecrypt().ToString();
        }

        public string ToString(string format)
        {
            return InternalDecrypt().ToString(format);
        }
    }
}